package org.qs.webservice.filter;

import com.sun.jersey.api.container.ContainerException;
import com.sun.jersey.api.core.HttpContext;
import com.sun.jersey.api.core.ResourceConfig;
import com.sun.jersey.core.util.ReaderWriter;
import com.sun.jersey.spi.container.ContainerRequest;
import com.sun.jersey.spi.container.ContainerRequestFilter;
import com.sun.jersey.spi.container.ContainerResponse;
import com.sun.jersey.spi.container.ContainerResponseFilter;
import com.sun.jersey.spi.container.ContainerResponseWriter;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Date;
import java.util.List;
import java.util.Map;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.HttpHeaders;
import javax.ws.rs.core.MultivaluedMap;

import org.apache.log4j.Logger;
import org.qs.webservice.util.IReadUtils;
import org.qs.webservice.util.StringUtils;
import org.qs.webservice.vo.UsLog;


public class IreadLogFilter implements ContainerRequestFilter, ContainerResponseFilter {
    public static final String FEATURE_LOGGING_DISABLE_ENTITY
            = "com.sun.jersey.config.feature.logging.DisableEntitylogging";

    private static final Logger LOGGER = Logger.getLogger(IreadLogFilter.class.getName());

    private static final String NOTIFICATION_PREFIX = "* ";
    
   // private static final String REQUEST_PREFIX = "> ";
    
    private static final String RESPONSE_PREFIX = "< ";

    private final Logger logger;

    private @Context HttpContext hc;
    private @Context HttpHeaders header;
    private @Context ResourceConfig rc;
    private @Context HttpServletRequest httpRequest;
    private @Context HttpServletResponse httpResponse;

    private long id = 0;
    
    private Date date;
    /**
     * Create a logging filter logging the request and response to
     * a default JDK logger, named as the fully qualified class name of this
     * class.
     */
    public IreadLogFilter() {
        this(LOGGER);
    }

    /**
     * Create a logging filter logging the request and response to
     * a JDK logger.
     *
     * @param logger the logger to log requests and responses.
     */
    public IreadLogFilter(Logger logger) {
        this.logger = logger;
    }

    private synchronized void setId() {
        if ( hc.getProperties().get("request-id") == null) {
            hc.getProperties().put("request-id", Long.toString(++id));
        }
    }

    private StringBuilder prefixId(StringBuilder b) {
        b.append(hc.getProperties().get("request-id").toString()).
                append(" ");
        return b;
    }
    
    public ContainerRequest filter(ContainerRequest request) {
    	
        setId();
        date=new Date();
        final StringBuilder b = new StringBuilder();	
        if (rc.getFeature(FEATURE_LOGGING_DISABLE_ENTITY)) {
            logger.info(b.toString());
            return request;
        } else {
            ByteArrayOutputStream out = new ByteArrayOutputStream();
            InputStream in = request.getEntityInputStream();
            try {
                if(in.available() > 0) {
                    ReaderWriter.writeTo(in, out);

                    byte[] requestEntity = out.toByteArray();
                    printEntity(b, requestEntity);

                    request.setEntityInputStream(new ByteArrayInputStream(requestEntity));
                }
                return request;
            } catch (IOException ex) {
                throw new ContainerException(ex);
            } finally {
                //logger.info(b.toString());
            }
        }
    }
    
  /*  private String printRequestLine(StringBuilder b, ContainerRequest request) {
    	//注释
       // prefixId(b).append(NOTIFICATION_PREFIX).append("Server in-bound request").append('\n');
        //url   1 > GET http://localhost:8080/api/page/content?page_id=1&pages=1&blocks=1
        prefixId(b).append(REQUEST_PREFIX).append(request.getMethod()).append(" ").
                append(request.getRequestUri().toASCIIString()).append('\n');
        return request.getRequestUri().toASCIIString();
    }*/
    
    public UsLog printRequestHeaders(StringBuilder b, MultivaluedMap<String, String> headers) {
    	UsLog usLog = new UsLog();
    	
    	logger.info(IReadUtils.beanToJson(headers));
    	//info-model
    	List<String> infoModels = headers.get("info-model");
    	if(infoModels!=null&&infoModels.size()>0){
    		usLog.setInfo_model(infoModels.get(0));
    	}else{
    		usLog.setInfo_model(null);
    	}
    	//info_packagename
    	List<String> info_packagenames = headers.get("info-packagename");
    	if(info_packagenames!=null&&info_packagenames.size()>0){
    		usLog.setInfo_packagename(info_packagenames.get(0));
    	}else{
    		usLog.setInfo_packagename(null);
    	}
    	//info-imsi
    	List<String> infoImsis = headers.get("info-imsi");
    	if(infoImsis!=null&&infoImsis.size()>0){
    		usLog.setInfo_imsi(infoImsis.get(0));
    	}else{
    		usLog.setInfo_imsi(null);
    	}
    	//info-vcode
    	List<String> infoVcodes = headers.get("info-vcode");
    	if(infoVcodes!=null&&infoVcodes.size()>0){
    		usLog.setInfo_vcode(infoVcodes.get(0));
    	}else{
    		usLog.setInfo_vcode(null);
    	}
    	//user-agent
    	List<String> userAgents = headers.get("user-agent");
    	if(userAgents!=null&&userAgents.size()>0){
    		usLog.setUser_agent(userAgents.get(0));
    	}else{
    		usLog.setUser_agent(null);
    	}
    	//info-imei
    	List<String> infoImeis = headers.get("info-imei");
    	if(infoImeis!=null&&infoImeis.size()>0){
    		usLog.setInfo_imei(infoImeis.get(0));
    	}else{
    		usLog.setInfo_imei(null);
    	}
    	//requestType
    	List<String> access_control_request_methods = headers.get("access-control-request-method");
    	if(access_control_request_methods!=null&&access_control_request_methods.size()>0){
    		usLog.setAccess_control_request_method(access_control_request_methods.get(0));
    	}else{
    		usLog.setAccess_control_request_method(null);
    	}
    	//info-platform
    	List<String> infoPlatforms = headers.get("info-platform");
    	if(infoPlatforms!=null&&infoPlatforms.size()>0){
    		if("android".equals(infoPlatforms.get(0)))
    			usLog.setInfo_platform("1");
    		else if("iOS".equals(infoPlatforms.get(0)))
    			usLog.setInfo_platform("2");
    		else if("ImreadH5".equals(infoPlatforms.get(0)))
    			usLog.setInfo_platform("3");
    		else if("android_sdk".equals(infoPlatforms.get(0)))
    			usLog.setInfo_platform("4");
    		else if("iOS_sdk".equals(infoPlatforms.get(0)))
    			usLog.setInfo_platform("5");
    		else if("android_single_yw".equals(infoPlatforms.get(0)))
    			usLog.setInfo_platform("6");
    		else if("2016sH5".equals(infoPlatforms.get(0)))
    			usLog.setInfo_platform("7");
    		else if("android_single".equals(infoPlatforms.get(0)))
        		usLog.setInfo_platform("8");
        	else if("android_single_qm".equals(infoPlatforms.get(0))){
            	usLog.setInfo_platform("9");
    		}else
    			usLog.setInfo_platform(infoPlatforms.get(0));
    	}else{
    		usLog.setInfo_platform("");
    	}
    	//info-platform
    	List<String> infoClients = headers.get("info-clientid");
    	if(infoClients!=null&&infoClients.size()>0){
    		usLog.setInfo_clientid(infoClients.get(0));
    		if(StringUtils.isNullOrEmpty(usLog.getInfo_clientid())){
    			usLog.setInfo_clientid(null);
    		}
    	}else{
    		usLog.setInfo_clientid(null);
    	}
    	//curtime
    	List<String> Curtimes = headers.get("curtime");
    	if(Curtimes!=null&&Curtimes.size()>0){
    		usLog.setCurtime(Curtimes.get(0));
    	}else{
    		usLog.setCurtime(null);
    	}
    	//appid
    	List<String> appids = headers.get("info-appid");
    	if(appids!=null&&appids.size()>0){
    		usLog.setInfo_appid(appids.get(0));
    	}else{
    		usLog.setInfo_appid(null);
    	}
    	//Token
    	List<String> tokens = headers.get("token");
    	if(tokens!=null&&tokens.size()>0){
    		usLog.setToken(tokens.get(0));
    	}else{
    		usLog.setToken(null);
    	}
    	//referer
    	List<String> referers = headers.get("referer");
    	if(referers!=null&&referers.size()>0){
    		usLog.setReferer(referers.get(0));
    	}else{
    		usLog.setReferer(null);
    	}
    	//info-userid
    	List<String> infoUserids = headers.get("info-userid");
    	if(infoUserids!=null&&infoUserids.size()>0){
    		usLog.setInfo_userid(infoUserids.get(0));
    	}else{
    		usLog.setInfo_userid(null);
    	}
    	//info-version
    	List<String> infoVersions = headers.get("info-version");
    	if(infoVersions!=null&&infoVersions.size()>0){
    		usLog.setInfo_version(infoVersions.get(0));
    	}else{
    		usLog.setInfo_version(null);
    	}
    	//info-channel
    	List<String> infoChannels = headers.get("info-channel");
    	if(infoChannels!=null&&infoChannels.size()>0){
    		usLog.setInfo_channel(infoChannels.get(0));
    	}else{
    		usLog.setInfo_channel(null);
    	}
    	//info-os
    	List<String> infoOss = headers.get("info-os");
    	if(infoOss!=null&&infoOss.size()>0){
    		usLog.setInfo_os(infoOss.get(0));
    	}else{
    		usLog.setInfo_os(null);
    	}
    	//info-uuid
    	List<String> infoUuids = headers.get("info-uuid");
    	if(infoUuids!=null&&infoUuids.size()>0){
    		usLog.setInfo_uuid(infoUuids.get(0));
    	}else{
    		usLog.setInfo_uuid(null);
    	}
    	//widthheight
    	List<String> widthheights = headers.get("widthheight");
    	if(widthheights!=null&&widthheights.size()>0){
    		usLog.setWidthheight(widthheights.get(0));
    	}else{
    		usLog.setWidthheight(null);
    	}
    	//getHost
    	List<String> hosts = headers.get("host");
    	if(hosts!=null&&hosts.size()>0){
    		usLog.setHost(hosts.get(0));
    	}else{
    		usLog.setHost(null);
    	}
    	//connection
    	List<String> connections = headers.get("connection");
    	if(connections!=null&&connections.size()>0){
    		usLog.setConnection(connections.get(0));
    	}else{
    		usLog.setConnection(null);
    	}
    	//accept-encoding
    	List<String> infoEncodings = headers.get("accept-encoding");
    	if(infoEncodings!=null&&infoEncodings.size()>0){
    		usLog.setAccept_encoding(infoEncodings.get(0));
    	}else{
    		usLog.setAccept_encoding(null);
    	}
    	//x-real-ip
    	List<String> x_real_ips = headers.get("x-real-ip");
    	if(x_real_ips!=null&&x_real_ips.size()>0){
    		usLog.setX_real_ip(x_real_ips.get(0));
    	}else{
    		usLog.setX_real_ip(null);
    	}
      /*  for (Map.Entry<String, List<String>> e : headers.entrySet()) {
            String header = e.getKey();
            for (String value : e.getValue()) {
                prefixId(b).append(REQUEST_PREFIX).append(header).append(": ").
                        append(value).append('\n');
            }
        }*/
        //prefixId(b).append(REQUEST_PREFIX).append('\n');
        return usLog;
    }

    private void printEntity(StringBuilder b, byte[] entity) throws IOException {
        if (entity.length == 0)
            return;
        b.append(new String(entity)).append("\n");
    }

    private final class Adapter implements ContainerResponseWriter {
        private final ContainerResponseWriter crw;

        private final boolean disableEntity;

        private long contentLength;

        private ContainerResponse response;

        private ByteArrayOutputStream baos;

        private StringBuilder b = new StringBuilder();

        Adapter(ContainerResponseWriter crw) {
            this.crw = crw;
            this.disableEntity = rc.getFeature(FEATURE_LOGGING_DISABLE_ENTITY);
        }
        
        public OutputStream writeStatusAndHeaders(long contentLength, ContainerResponse response) throws IOException {
            printResponseLine(b, response);
            printResponseHeaders(b, response.getHttpHeaders());

            if (disableEntity) {
                logger.info(b.toString());
                return crw.writeStatusAndHeaders(contentLength, response);
            } else {
                this.contentLength = contentLength;
                this.response = response;
                return this.baos = new ByteArrayOutputStream();
            }
        }

        public void finish() throws IOException {
        	String referer = "";
        	referer = httpRequest.getHeader("Origin");
        	if(!StringUtils.isNullOrEmpty(referer)){
        		String[] allowReferer = {
            			"m.imread.com",
            			"192.168.0.249:8000",
            			"192.168.0.249:8081",
            			"192.168.0.249:8080",
            			"192.168.0.249:8020",
            			"192.168.0.251:8000",
            			"192.168.0.251:8080",
            			"192.168.0.251:8020",
            			"192.168.0.34:9090",
            			"192.168.0.34:8080",
            			"192.168.0.252:8000",
            			"192.168.1.251:8000",
            			"192.168.1.251:8080",
            			"192.168.1.249:8000",
            			"192.168.1.249:8080",
            			"192.168.1.249:8081",
            			"192.168.1.102:80",
            			"219.82.225.136:80",
            			"www.imread.com.cn",
            			"imread.com.cn"
            	};
        		httpResponse.setHeader("Access-Control-Allow-Origin", "*");  
            	/*for(String r:allowReferer){
            		if(referer.startsWith("http://"+r)){
            			httpResponse.setHeader("Access-Control-Allow-Origin", "*");  
            			break;
            		}
            	}*/
            	
        	}
        	httpResponse.setHeader("Access-Control-Allow-Headers","Info-Imsi,Info-appid,Info-Imei,Info-Channel,Info-Version,Info-Model,Info-Os,info-platform,Info-Vcode,Info-Userid,Info-Uuid,Token,Info-Resolution,Info-ClientId,Curtime,WidthHeight,isFree");
            if (!disableEntity) {
                byte[] entity = baos.toByteArray();
                printEntity(b, entity);
                // Output to log
               // logger.info(b.toString());W
                // Write out the headers and buffered entity
                OutputStream out = crw.writeStatusAndHeaders(contentLength, response);
                out.write(entity);
               // logger.info("finish-end");
               //logger.info(b.toString());
                Date recode = new Date();
                UsLog usLog = printRequestHeaders(b, header.getRequestHeaders());
               // CustomerLog.customerLog(logger,url);
              //  CustomerLog.customerLog(logger,usLog.getInfo_platform()+
              //  		"|"+httpRequest.getRemoteAddr()+"|"+usLog.getHost()+" | "+usLog.getCurtime()+" | "+recordTimeLen+" | "+usLog.getReferer()+" | "+pageUrl+" | "+user_agent+" | "+clientVersion+" | "+requestedType);
                //门户类型     对端IP地址   门户本机IP地址   记录时间   处理时长  来源地址 访问的完整url  用户原始UA信息   客户端版本号   请求类型  用户ID 设备号  渠道代码   imsi  Info_model 适配后的页面宽高比   适配后的操作系统     Info_uuid  clientid info_vode
                String userId = IReadUtils.getUserIdByTokenWithoutEffective(usLog.getToken());
                String ret=httpRequest.getQueryString();
                if(StringUtils.isNullOrEmpty(ret)){
                	ret = IReadUtils.beanToJson(httpRequest.getParameterMap());
                	if("{}".equals(ret)){
                		ret="";
                	}
                }else{
                	ret="?"+ret;
                }
                if(StringUtils.isNullOrEmpty(userId)){
                	userId=null;
                }
                if(!httpRequest.getRequestURL().toString().endsWith("intercut/log"))
	                if(StringUtils.isNullOrEmpty(usLog.getAccess_control_request_method())){
	                	  CustomerLog.customerLog(logger,usLog.getInfo_platform()+
	                      		"|"+usLog.getX_real_ip()+"|101.200.191.41|"+date.getTime()+"|"+(recode.getTime()-date.getTime())+
	                      		"|"+usLog.getReferer()+"|"+httpRequest.getRequestURL()+ret+"|"+usLog.getUser_agent()+
	                      		"|"+usLog.getInfo_version()+"|"+usLog.getRequestType()+"|"+userId);
	            
	                }
            }
            crw.finish();
        }
    }

    public ContainerResponse filter(ContainerRequest request, ContainerResponse response) {
        setId();
        
        response.setContainerResponseWriter(
                new Adapter(response.getContainerResponseWriter()));
        return response;
    }
    
    private void printResponseLine(StringBuilder b, ContainerResponse response) {
        prefixId(b).append(NOTIFICATION_PREFIX).
            append("Server out-bound response").append('\n');
        prefixId(b).append(RESPONSE_PREFIX).append(Integer.toString(response.getStatus())).append('\n');
    }
    
    private void printResponseHeaders(StringBuilder b, MultivaluedMap<String, Object> headers) {
        for (Map.Entry<String, List<Object>> e : headers.entrySet()) {
            String header = e.getKey();
            for (Object value : e.getValue()) {
                prefixId(b).append(RESPONSE_PREFIX).append(header).append(": ").
                        append(ContainerResponse.getHeaderValue(value)).append('\n');
            }
        }
        prefixId(b).append(RESPONSE_PREFIX).append('\n');
    } 
}